<?php

namespace Controller;

use Core\Controller;
use Core\Exception;
use Core\Di;
use Log\SeasLog;
use think\Db;
use UserModel;
use Service\AppTokenService;
use Service\ResponseService;
use Helper\ArrayHelper;
use Extend\KeyWorker;


/**
 * APP控制器基类
 * Class Apps
 * @package Controller
 */
class Apps extends Controller
{
    /**
     * 接口允许时间差
     * @var int
     */
    private $leeway = 300;

    private $rsaKey = [];

    public $params = [];

    public $sign = '';

    public $token = '';

    public $clientInfo = [];

    public $userInfo = [];

    /**
     * 需要登录权限的控制器
     * ‘*’号代表改控制下所有action都要登录权限(全部小写)
     * @var array
     */
    protected $needLoginActions = [];

    /**
     *
     * @throws Exception
     */
    public function init()
    {
        parent::init();
        $this->rsaKey     = Di::get('config')['secretKey'];
        $requestData      = $this->request->post('', '', false);//绕过参数过滤规则
        $this->token      = $this->request->getServer('HTTP_AUTHORIZATION');
        $this->params     = json_decode($requestData['reqdata'], true);
        $this->clientInfo = json_decode($_POST['clientInfo'], true);
        $this->sign       = $requestData['sign'];
//        SeasLog::info(json_encode($requestData));
        $this->baseCheck();
        $this->needLoginCheck();

    }

    /**
     * 成功返回
     * @param string $msg
     * @param null $data
     * @param string $header
     * @param string $token
     */
    public function success($msg = '', $data = null, $header = '', $token = '')
    {
        $message = $msg ? $msg : 'success';
        if (!empty($token)) {
            $this->token = $token;
        }
        if (is_array($data)) {
            $data = ArrayHelper::nullToEmpty($data);
        }
        $params        = json_encode($data, 320);
        $privateWorker = new KeyWorker($this->rsaKey['serverPrivateKey'], KeyWorker::ASN);
        $sign          = $privateWorker->sign($params, $privateWorker->getKey());
        ResponseService::json(1, $message, ['token' => $this->token, 'data' => $data, 'sign' => $sign], $header, $message);

    }

    /**
     * 错误返回
     * @param int $code
     * @param string $msg
     * @param string $message
     */
    public function error($code = 0, $msg = '', $data = [], $debugMsg = '')
    {
        ResponseService::json($code, $msg, $data, [], $debugMsg);
    }

    /**
     * 接口规范、基础格式检查
     * @throws Exception
     */
    private function baseCheck()
    {
        // 基本格式检查
        if (empty($this->params) OR !is_array($this->params) OR empty($this->sign) OR empty($this->clientInfo)) {
            throw new Exception(101281, '接口格式错误');
        }
        // sign检查
        if (!DEBUG AND strtolower($this->_name) != 'appversion') {
            $publicWorker = new KeyWorker($this->rsaKey['clientPublicKey'], KeyWorker::ASN);
            $check        = $publicWorker->verify($this->request->post('reqdata', '', false), $this->sign, $publicWorker->getKey());
            if (!$check) {
                SeasLog::error('验签失败' . ' | ' . 'reqdata:' . $this->request->post('reqdata') . 'sing:' . $this->sign);
                throw new Exception(101282, '签名错误');
            }
        }
        // 请求时间检查
        if (!DEBUG) {
            if (empty($this->params['t'])) {
                throw new Exception(100073, '请求超时');
            }
            $t = $this->params['t'];
            if (strlen($t) == 13) {
                $t = (int)($t / 1000);
            }
            if (strlen($t) != 10 OR $t - time() > $this->leeway) {
                throw new Exception(100074, '请求超时');
            }
        }
        return true;
    }

    /**
     * 用户token检查，通过
     *
     * @throws
     */
    private function tokenCheck()
    {
        if (!empty($this->token)) {
            // 通过token获取userId
            $payload = AppTokenService::getPayloadByToken($this->token);
            if ($payload && !empty($payload['aud']) && gettype($payload['aud']) != 'object') {
                if (!empty($payload['exp']) AND $payload['exp'] > time()) {
                    $userId = $payload['aud'];
                    // 更新userId信息
                    $userModel = new UserModel();
                    $userInfo  = $userModel->getUserById($userId);
                    if (!empty($userInfo)) {
                        //保存用户的设备信息
                        $this->userDeviceLog($userId);
                        $this->userInfo = $userInfo;
                        // 刷新token
                        $this->token = AppTokenService::create($userId);
                        return true;
                    }
                }
            }
        }
        return false;
    }


    /**
     * 判断控制器是否需要登录
     * @throws
     */
    private function needLoginCheck()
    {
        # 1.检查该控制器是否需要登录
        $needLogin = false;
        if (!empty($this->needLoginActions)) {
            if (in_array('*', $this->needLoginActions)) {
                $needLogin = true;
            } else {
                $actionName = strtolower($this->getRequest()->getActionName());
                if (!empty($actionName) AND in_array($actionName, $this->needLoginActions)) {
                    $needLogin = true;
                }
            }
        }
        # 2.检查token
        $tokenCheck = $this->tokenCheck();
        if ($needLogin AND !$tokenCheck) {
            throw new Exception(10003, '登录过期,请重新登录');
        }
        # 3.检查用户是否已注销
        if ($this->userInfo['is_logoff'] == 1) {
            throw new Exception(10004);
        }

        # 4.检查后台有没有强制踢出
//        try {
//            $redis = Di::get('Redis');
//            $redis->select(3);
//        } catch (\Exception $e) {
//            throw new Exception(10345, $e->getMessage());
//        }
//        if ($redis->exists('blacklist')) {
//            $inBlacklist = $redis->sIsMember('blacklist', $this->userInfo['id']);
//            if($inBlacklist){
//                $redis->sRem('blacklist', $this->userInfo['id']);
//                throw new Exception(10003,'后台强制踢出,请重新登录');
//            }
//        }
        return true;
    }

    private function userDeviceLog($userId)
    {
        $userDeviceLog = Db::name('user_device_log')
            ->where(['user_id' => $userId, 'device_id' => $this->clientInfo['deviceId']])
            ->find();
        $info          = [
            'device_name' => $this->clientInfo['deviceName'] ?: '',
            'device_id'   => $this->clientInfo['deviceId'] ?: '',
            'update_time' => time(),
            'add_time'    => time(),
            'version'     => $this->clientInfo['version'] ?: 0,
            'platform'    => $this->clientInfo['platform'] ?: '',
            'versionCode' => $this->clientInfo['versionCode'] ?: '',
            'user_id'     => $userId,
        ];
        if (!empty($userDeviceLog)) {
            unset($info['add_time']);
            Db::name('user_device_log')
                ->where(['user_id' => $userId, 'device_id' => $this->clientInfo['deviceId']])
                ->update($info);
        } else {
            Db::name('user_device_log')->insert($info);
        }
        return true;

    }


}